package com.mini.rpc.handler;

import com.mini.rpc.common.MiniRpcRequest;
import com.mini.rpc.common.MiniRpcResponse;
import com.mini.rpc.common.RpcServiceHelper;
import com.mini.rpc.protocol.MiniRpcProtocol;
import com.mini.rpc.protocol.MsgHeader;
import com.mini.rpc.protocol.MsgStatus;
import com.mini.rpc.protocol.MsgType;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.reflect.FastClass;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;

/**
 * 执行 RPC 请求调用
 */
@Slf4j
public class RpcRequestHandler extends SimpleChannelInboundHandler<MiniRpcProtocol<MiniRpcRequest>> {

    /**
     * key:  value
     * 例如: key: com.rpc.service.HelloImpl#1.0.0
     * .     value: new HelloImpl()
     */
    private final Map<String, Object> rpcServiceMap;

    public RpcRequestHandler(Map<String, Object> rpcServiceMap) {
        this.rpcServiceMap = rpcServiceMap;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MiniRpcProtocol<MiniRpcRequest> protocol) {
        // 由于 RPC 请求操作是比较耗时的, 所有将任务提交到自定义线程池中执行
        RpcRequestProcessor.submitRequest(() -> {
            MiniRpcProtocol<MiniRpcResponse> resProtocol = new MiniRpcProtocol<>();
            MiniRpcResponse response = new MiniRpcResponse();
            MsgHeader header = protocol.getHeader();
            header.setMsgType((byte) MsgType.RESPONSE.getType());
            try {
                Object result = handle(protocol.getBody());
                response.setData(result);

                header.setStatus((byte) MsgStatus.SUCCESS.getCode());
            } catch (Throwable throwable) {
                header.setStatus((byte) MsgStatus.FAIL.getCode());
                String msg;
                if (throwable instanceof InvocationTargetException) {
                    InvocationTargetException ivcExp = (InvocationTargetException) throwable;
                    // todo targetExp.getCause().getMessage() 为什么这是会有error呢?
                    msg = "Caused by: " + ivcExp.getTargetException().toString();
                } else {
                    msg = "Caused by: " + throwable.toString() + ": " + throwable.getCause().getMessage();
                }
                log.error("error: {}", msg);
                // response.setData(null);
                response.setMessage(msg);
                log.error("process request {} error", header.getRequestId(), throwable);
            } finally {
                resProtocol.setHeader(header);
                resProtocol.setBody(response);
            }
            ctx.writeAndFlush(resProtocol);
        });
    }

    /**
     * 真正执行 RPC 调用的方法
     */
    private Object handle(MiniRpcRequest request) throws Throwable {
        String serviceKey = RpcServiceHelper.buildServiceKey(request.getClassName(), request.getServiceVersion());
        Object serviceBean = rpcServiceMap.get(serviceKey);

        if (serviceBean == null) {
            throw new RuntimeException(String.format("service not exist: %s:%s", request.getClassName(), request.getMethodName()));
        }

        Class<?> serviceClass = serviceBean.getClass();
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParams();

        // 采用 Cglib 提供的 FastClass 机制直接调用方法 (非反射方式)
        FastClass fastClass = FastClass.create(serviceClass);
        int methodIndex = fastClass.getIndex(methodName, parameterTypes);
        log.info(">>> rpc call. method: {}.{}  params: {} ", request.getClassName(), methodName, Arrays.toString(parameters));
        return fastClass.invoke(methodIndex, serviceBean, parameters);
    }
}
